seating.js ➔ select_ticket   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
dl 0
loc 16
rs 9.85
c 0
b 0
f 0
1
/**
2
 * Make tooltips appear on hover over seats.
3
 */
4
function init_seat_tooltips() {
5
  document.querySelectorAll('.seat-with-tooltip')
6
    .forEach(seatContainer => {
7
      seatContainer.addEventListener('mouseover', () => {
8
        const dataset = seatContainer.dataset;
9
10
        let tooltipHTML = '<div class="seat-label">' + dataset.label + '</div>';
11
12
        const ticketId = dataset.ticketId;
13
        if (ticketId !== undefined) {
14
          if (seatContainer.querySelector('.seat').classList.contains('seat--managed')) {
15
            const ticketCode = document
16
              .getElementById('ticket-selection')
17
              .querySelector('.ticket[data-id="' + ticketId + '"]')
18
              .dataset.code;
19
20
            tooltipHTML += '<div class="seat-ticket-code"><span class="dimmed">Ticket:</span> <strong>' + ticketCode + '</strong></div>';
21
          }
22
23
          const occupierName = dataset.occupierName;
24
          if (occupierName !== undefined) {
25
            tooltipHTML += '<div class="seat-occupier">'
26
                         + '<div class="seat-occupier-avatar"><div class="avatar size-48"><img src="' + dataset.occupierAvatar + '"></div></div>'
27
                         + '<div class="seat-occupier-name"><span class="dimmed">reserviert von</span><br><strong>' + escape_html(occupierName) + '</strong></div>'
28
                         + '</div>';
29
          }
30
        }
31
32
        const tooltipNode = document.createElement('div');
33
        tooltipNode.classList.add('seat-tooltip');
34
        tooltipNode.innerHTML = tooltipHTML;
35
        seatContainer.appendChild(tooltipNode);
36
      });
37
38
      seatContainer.addEventListener('mouseout', () => {
39
        const tooltip = seatContainer.querySelector('.seat-tooltip');
40
        // Tooltip element won't exist at this point if the mouse
41
        // cursor is above a seat when the page is reloaded, so the
42
        // 'mouseout' event will fire upon mouse move even though no
43
        // 'mouseover' has occurred, thus no tooltip has been
44
        // generated that could be removed.
45
        if (tooltip !== null) {
46
          tooltip.remove();
47
        }
48
      });
49
    });
50
}
51
52
53
/**
54
 * Initialize the seat management system.
55
 */
56
function init_seat_management(selected_ticket_id) {
57
  init_ticket_selector();
58
59
  const tickets = document.querySelectorAll('.ticket');
60
  if (tickets.length > 0) {
61
    // Pre-select ticket.
62
    let ticket_to_preselect;
63
    if (selected_ticket_id !== null) {
64
      ticket_to_preselect = find_ticket_by_id(selected_ticket_id);
65
    } else {
66
      // Select first ticket by default.
67
      ticket_to_preselect = tickets[0];
68
    }
69
    select_ticket(ticket_to_preselect);
70
71
    mark_seats_as_occupiable();
72
    init_occupiable_seats();
73
    mark_managed_seats();
74
    mark_seat_for_selected_managed_ticket();
75
    wire_seat_release_button();
76
  }
77
}
78
79
80
/**
81
 * Make ticket selector work.
82
 */
83
function init_ticket_selector() {
84
  _make_ticket_selector_open_on_click();
85
  _select_ticket_on_click();
86
}
87
88
89
/**
90
 * Make ticket selector open on click.
91
 */
92
function _make_ticket_selector_open_on_click() {
93
  const ticket_selector = document.querySelector('.ticket-selector');
94
  if (ticket_selector !== null) {
95
    ticket_selector
96
      .addEventListener('click', () => ticket_selector.classList.toggle('ticket-selector--open'));
97
  }
98
}
99
100
101
/**
102
 * Select ticket on click on ticket in ticket selector.
103
 */
104
function _select_ticket_on_click() {
105
  document.querySelectorAll('.ticket')
106
    .forEach(ticket => ticket.addEventListener('click', () => select_ticket(ticket)));
107
}
108
109
110
/**
111
 * Return the ticket node in the selector with that ticket ID,
112
 * or `null` if not found.
113
 */
114
function find_ticket_by_id(ticket_id) {
115
  const ticket = document
116
    .getElementById('ticket-selection')
117
    .querySelector('.ticket[data-id="' + ticket_id + '"]');
118
119
  if (ticket === null) {
120
    return null;
121
  }
122
123
  return ticket;
124
}
125
126
127
/**
128
 * Set the ticket as the selected one in the ticket selector (both
129
 * visually and regarding its internal state).
130
 */
131
function select_ticket(ticket) {
132
  const ticket_id = ticket.dataset.id;
133
  const ticket_code = ticket.dataset.code;
134
135
  const ticket_selection = document.getElementById('ticket-selection');
136
  ticket_selection.dataset.selectedId = ticket_id;
137
  ticket_selection.dataset.selectedCode = ticket_code;
138
  ticket_selection.querySelectorAll('.ticket--current')
139
    .forEach(ticket => ticket.classList.remove('ticket--current'));
140
141
  ticket.classList.add('ticket--current');
142
143
  set_current_managed_seat(ticket_id);
144
145
  document.getElementById('release-seat-trigger').disabled = !is_ticket_occupying_seat(ticket);
146
}
147
148
149
function set_current_managed_seat(ticket_id) {
150
  document.querySelectorAll('.area .seat--managed-current')
151
    .forEach(seat => seat.classList.remove('seat--managed-current'));
152
153
  document.querySelectorAll('.seat-with-tooltip[data-ticket-id="' + ticket_id + '"] .seat--managed')
154
    .forEach(seat => seat.classList.add('seat--managed-current'));
155
}
156
157
158
function wire_seat_release_button() {
159
  const release_seat_trigger = document.getElementById('release-seat-trigger');
160
  if (release_seat_trigger !== null) {
161
    release_seat_trigger.addEventListener('click', () => {
162
      const seat_label = get_selected_seat_label();
163
      const confirmation_label = seat_label + ' (belegt durch Ticket ' + get_selected_ticket_code() + ') freigeben?';
164
      if (confirm(confirmation_label)) {
165
        const ticket_id = get_selected_ticket_id();
166
167
        const request_url = '/seating/ticket/' + ticket_id + '/seat';
168
169
        fetch(request_url, {method: 'DELETE'})
170
          .then(response => reload_with_selected_ticket(ticket_id));
171
      };
172
      return false;
173
    });
174
  }
175
}
176
177
178
function get_selected_ticket_id() {
179
  return document.getElementById('ticket-selection').dataset.selectedId;
180
}
181
182
183
function get_selected_ticket_code() {
184
  return document.getElementById('ticket-selection').dataset.selectedCode;
185
}
186
187
188
function is_ticket_occupying_seat(ticket) {
189
  return ticket.dataset.seatLabel !== undefined;
190
}
191
192
193
function get_selected_seat_label() {
194
  const ticket_selection = document.getElementById('ticket-selection');
195
  const ticket_code = ticket_selection.dataset.selectedCode;
196
  const ticket = ticket_selection.querySelector('.ticket[data-code="' + ticket_code + '"]');
197
  return ticket.dataset.seatLabel;
198
}
199
200
201
function get_managed_ticket_ids() {
202
  const managed_ticket_ids = new Set();
203
204
  document.getElementById('ticket-selection')
205
    .querySelectorAll('.ticket')
206
    .forEach(ticket => managed_ticket_ids.add(ticket.dataset.id));
207
208
  return managed_ticket_ids;
209
}
210
211
212
function get_seats_with_ticket_code(ticket_id) {
213
  return document.querySelectorAll('.seat-with-tooltip[data-ticket-id="' + ticket_id + '"] .seat');
214
}
215
216
217
function mark_managed_seats() {
218
  const managed_ticket_ids = get_managed_ticket_ids();
219
220
  for (const ticket_id of managed_ticket_ids.values()) {
221
    get_seats_with_ticket_code(ticket_id)
222
      .forEach(seat => seat.classList.add('seat--managed'));
223
  }
224
}
225
226
227
function mark_seat_for_selected_managed_ticket() {
228
  const ticket_id = get_selected_ticket_id();
229
  get_seats_with_ticket_code(ticket_id)
230
    .forEach(seat => seat.classList.add('seat--managed-current'));
231
}
232
233
234
function mark_seats_as_occupiable() {
235
  document.querySelectorAll('.seat')
236
    .forEach(seat => {
237
      const ticket_id = seat.parentNode.dataset.ticketId;
238
      if (ticket_id === undefined) {
239
        seat.classList.add('seat--occupiable');
240
      }
241
    }
242
  );
243
}
244
245
246
function init_occupiable_seats() {
247
  document.querySelectorAll('.seat--occupiable')
248
    .forEach(seat => {
249
      seat.addEventListener('click', () => {
250
        const seat_label = seat.parentNode.dataset.label;
251
        const confirmation_label = seat_label + ' mit Ticket ' + get_selected_ticket_code() + ' reservieren?';
252
        if (confirm(confirmation_label)) {
253
          const seat_id = seat.parentNode.dataset.seatId;
254
          const ticket_id = get_selected_ticket_id();
255
256
          const request_url = '/seating/ticket/' + ticket_id + '/seat/' + seat_id;
257
258
          fetch(request_url, {method: 'POST'})
259
            .then(response => reload_with_selected_ticket(ticket_id));
260
        };
261
        return false;
262
      });
263
    });
264
}
265
266
267
function reload_with_selected_ticket(ticket_id) {
268
  location.href = location.href.split('?')[0] + '?ticket_id=' + ticket_id;
269
}
270
271
272
/**
273
 * Use browser's built-in functionality to quickly and safely escape
274
 * HTML in the given string.
275
 */
276
function escape_html(str) {
277
  const elem = document.createElement('span');
278
  elem.appendChild(document.createTextNode(str));
279
  return elem.innerHTML;
280
}
281